package xuegao.study.mall.rbac.service.impl;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import xuegao.study.mall.common.model.ums.UmsDepartment;
import xuegao.study.mall.common.vo.ums.UmsDepartmentVo;
import xuegao.study.mall.rbac.common.ErrorMsgConstant;
import xuegao.study.mall.rbac.service.UmsDepartmentService;
import xuegao.study.mall.rbac.util.EasyExcelUtils;
import xuegao.study.mall.rbac.util.UmsDepartmentExcelInfo;
import xuegao.study.mall.repo.dao.ums.UmsDepartmentDao;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class UmsDepartmentServiceImpl implements UmsDepartmentService {

    private static final Logger LOGGER = LoggerFactory.getLogger(UmsDepartmentServiceImpl.class);

    private static final Long TOP_DEP_PID = 0L;

    private static final Integer TOP_DEP_LEVEL = 0;

    private static final String TOP_DEP_CODE = "100";

    @Autowired
    private UmsDepartmentDao umsDepartmentDao;

    @Override
    public int save(UmsDepartment department) {
        // 1.name唯一性校验
        Map<String, Object> existsQueryParams = new HashMap<>();
        existsQueryParams.put("name", department.getName());
        List<UmsDepartment> existsDepList = umsDepartmentDao.selectByMap(existsQueryParams);
        if (existsDepList != null && existsDepList.size() > 0) {
            String errMsg = String.format(ErrorMsgConstant.DEP_ALREADY_EXISTS, department.getName());
            throw new RuntimeException(errMsg);
        }
        // 2.level=0,pid设置为0,code设置为100
        Integer level = department.getLevel();
        if (level.equals(TOP_DEP_LEVEL)) {
            department.setPid(TOP_DEP_PID);
            department.setLevel(TOP_DEP_LEVEL);
            department.setCode(TOP_DEP_CODE);
        } else {
            // 先校验父节点是否存在
            Long pid = department.getPid();
            UmsDepartment parentDep = umsDepartmentDao.selectById(pid);
            if (null == parentDep) {
                String errMsg = String.format(ErrorMsgConstant.DEP_NOT_EXISTS, pid);
                throw new RuntimeException(errMsg);
            }
            // leve为父节点level加1
            department.setPid(parentDep.getId());
            department.setLevel(parentDep.getLevel() + 1);
            // 设置部门编码
            String maxCode = generateDepCode(parentDep.getId());
            department.setCode(maxCode);
        }
        // 生成部门code
        return umsDepartmentDao.insert(department);
    }

    @Override
    public int deleteById(Long id) {
        Map<String, Object> params = new HashMap<>();
        params.put("pid", id);
        List<UmsDepartment> childrenList = umsDepartmentDao.selectByMap(params);
        if (childrenList != null && childrenList.size() > 0) {
            String errMsg = ErrorMsgConstant.DEP_DEL_EXISTS_CHILD;
            throw new RuntimeException(errMsg);
        }
        return umsDepartmentDao.deleteById(id);
    }

    @Override
    public int update(UmsDepartment department) {
        UmsDepartment dep = umsDepartmentDao.selectById(department.getId());
        if (null == dep) {
            String errMsg = String.format(ErrorMsgConstant.DEP_NOT_EXISTS, department.getId());
            throw new RuntimeException(errMsg);
        }
        dep.setName(department.getName());
        dep.setDescription(department.getDescription());
        return umsDepartmentDao.updateById(dep);
    }

    @Override
    public List<UmsDepartment> findAll() {
        return umsDepartmentDao.selectByMap(new HashMap<>());
    }

    @Override
    public List<UmsDepartmentVo> listAll() {
        List<UmsDepartment> list = umsDepartmentDao.selectByMap(new HashMap<>());
        return findChildren(0L, list);
    }

    @Override
    public void export(HttpServletResponse response) throws IOException {
        String fileName = URLEncoder.encode("用户信息","UTF-8");
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
        ServletOutputStream out = response.getOutputStream();
        List<UmsDepartment> depList = umsDepartmentDao.selectByMap(new HashMap<>());
        List<UmsDepartmentExcelInfo> infoList = new ArrayList<>();
        for (UmsDepartment vo : depList) {
            UmsDepartmentExcelInfo info = new UmsDepartmentExcelInfo();
            BeanUtils.copyProperties(vo, info);
            infoList.add(info);
        }
        EasyExcelUtils.export(out, infoList);
    }

    @Override
    public void importDep(List<UmsDepartment> list) {
        // 1.循环依赖检查
        for (UmsDepartment dep : list) {
            try {
                cycleCheck(list, dep, 0, new StringBuilder());
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        // 2.存在性检测
        List<Long> notExistsIds = new ArrayList<>();
        for (UmsDepartment dep : list) {
            boolean exists = exists(list, dep.getPid());
            if (!exists) {
                notExistsIds.add(dep.getPid());
            }
        }
        if (notExistsIds.size() > 0) {
            LOGGER.error("not exists {}", notExistsIds);
            String errMsg = ErrorMsgConstant.DEP_DEPEND_ON_NOT_EXISTS;
            throw new RuntimeException(errMsg);
        }
        // 3.入库操作
        Map<String, Object> params = new HashMap<>();
        for (UmsDepartment dep : list) {
            params.put("id", dep.getPid());
            umsDepartmentDao.deleteByMap(params);
            umsDepartmentDao.insert(dep);
        }
    }

    /**
     * 递归获取部门列表
     * @param pid 父id
     * @param list 全量数据
     * @return 结果
     */
    public List<UmsDepartmentVo> findChildren(Long pid, List<UmsDepartment> list) {
        List<UmsDepartmentVo> voList = new ArrayList<>();
        if (null != list && list.size() > 0) {
            List<UmsDepartment> resList = list.stream().filter(p -> p.getPid().equals(pid)).collect(Collectors.toList());
            for (UmsDepartment dep : resList) {
                UmsDepartmentVo vo = new UmsDepartmentVo();
                BeanUtils.copyProperties(dep, vo);
                List<UmsDepartmentVo> children = findChildren(dep.getId(), list);
                if (children != null && children.size() > 0) {
                    vo.setChildren(children);
                } else {
                    vo.setLeaf(Boolean.TRUE);
                }
                voList.add(vo);
            }
        }
        return voList;
    }

    private void cycleCheck(List<UmsDepartment> list, UmsDepartment dep, int count, StringBuilder sb) throws Exception {
        for (UmsDepartment temp : list) {
            if (temp.getId().equals(dep.getPid())) {
                if (sb.length() > 0) {
                    sb.append("->");
                }
                sb.append(dep.getPid());
                count++;
                if (count > list.size()) {
                    String errMsg = String.format(ErrorMsgConstant.DEP_CYCLIC_DEP, sb);
                    throw new Exception(errMsg);
                }
                cycleCheck(list, temp, count, sb);
            }
        }
    }

    private boolean exists(List<UmsDepartment> list, Long pid) {
        UmsDepartment checkDep = null;
        if (TOP_DEP_PID.equals(pid)) {
            return true;
        }
        for (UmsDepartment temp : list) {
            if (temp.getId().equals(pid)) {
                checkDep = temp;
                break;
            }
        }
        if (checkDep == null) {
            checkDep = umsDepartmentDao.selectById(pid);
        }
        return checkDep != null;
    }

    /**
     * 生成部门编码
     * @param pid 父部门编码id
     * @return depCode
     */
    private String generateDepCode(Long pid) {
        if (pid.equals(0L)) {
            return TOP_DEP_CODE;
        }
        // 查询父部门下子部门的code最大值,将该code值加1
        UmsDepartment parentDep = umsDepartmentDao.selectById(pid);
        String maxCode = umsDepartmentDao.selectMaxCode(parentDep.getId());
        if (maxCode == null || "".equals(maxCode)) {
            maxCode = parentDep.getCode() + "00";
        } else {
            maxCode = String.valueOf(Long.parseLong(maxCode) + 1);
        }
        return maxCode;
    }
}
